from IPython.display import Image, SVG
Image(filename="title.png")
According to the EEA (European Environment Agency), air pollution is the biggest environmental health risk in Europe. Despite the fact that air quality levels have been improving over recent decades, the quantity of pollutants still exceeds EU standards and the most stringent World Health Organisation guidelines. Given that the majority of Europe and Spain's population are concentrated in urban areas, the number of people exposed to harmful levels of pollutants and at risk of developing respiratory problems, cardiovascular problems or cancer constitutes a grave public heatlth risk.
Barcelona, like other major European cities, has recognised the need to take action and has implemented measures to reduce the levels air pollution. At the start of this year the city council launched the Zona de Baixes Emissions to combat the number of pollutants release thanks to road traffic.
The city council has also been monitoring the levels of air pollution at different points in the city. This project takes a look at that data to compare how levels of emissions have changed in 2020, thanks in large part to Covid 19.
# @hidden_cell
import numpy as np
import pandas as pd
import plotly
import plotly.graph_objects as go
import folium
from folium.plugins import HeatMapWithTime
import datetime as dt
import os
import warnings
warnings.filterwarnings('ignore')
# @hidden_cell
os.chdir(r'C:\Users\Maria\DataViz_Comp')
data = pd.read_csv('2019_air_quality.csv', index_col=0)
# @hidden_cell
lat, lon = round(data.latitude.mean(), 2), round(data.longitude.mean(), 2)
barca_map = folium.Map(location=[lat, lon], zoom_start=13)
locations = data.location.unique()
for i, value in enumerate(zip(data.latitude.unique(), data.longitude.unique())):
folium.Marker(value, popup=locations[i]).add_to(barca_map)
barca_map.save('barca_map.html')
barca_map
The monitoring stations can be divided into different categories:
The sensors collect data on levels of 7 different air pollutants:
1) CO: Carbon monoxide
2) NO: Nitrogen monoxide / Nitric oxide
3) NO2: Nitrogen dioxide
4) NOx: Nitrogen oxides (collective term)
5) O3: Troposheric ozone
6) PM10: Particle matter with a diameter of less than 10 μm
7) SO2: Sulfur dioxide
This project looks at the levels of these pollutants registered by the sensors between April 2019 and September 2020. Not all stations show data for all particles, as can be seen in the table below.
# @hidden_cell
particles = pd.DataFrame(index=sorted(data.location.unique()), columns=sorted(data.particle.unique()),
data=[['x', 'YES', 'YES', 'YES', 'YES', 'YES', 'x'],
['YES', 'YES', 'YES', 'YES', 'YES', 'YES', 'YES'],
['YES', 'YES', 'YES', 'YES', 'YES', 'YES', 'YES'],
['x', 'YES', 'YES', 'YES', 'YES', 'YES', 'x'],
['YES', 'YES', 'YES', 'YES', 'YES', 'YES', 'YES'],
['x', 'YES', 'YES', 'YES', 'x', 'YES', 'x'],
['x', 'YES', 'YES', 'YES', 'x', 'YES', 'x'],
['YES', 'YES', 'YES', 'YES', 'YES', 'YES', 'YES']])
particles
There are no legal standards for these pollutants on a weekly or monthly basis. For NO2 and O3 the EU standards are based on hourly and annual averages, and for PM10 on daily and annual averages.
EU standards:
WHO guidelines:
O3 is a secondary pollutant formed from other pollutants in the presence of solar light. Its levels are determined by emissions and climatic conditions.
The graphs below show results from the period from the beginning of April 2019 to the end of September 2019.
In the daily averages graphs, we examine first the average hourly levels for the whole timeframe and then compare on a particle by particle basis the period "pre-covid" (up until confinement officially began on the 14th of March) and the period "post-covid" after that date.
# @hidden_cell
post_covid = data[data.year == 2020]
post_covid = post_covid[post_covid.month > 3]
post_covid = post_covid[post_covid.day > 14]
# @hidden_cell
df_2019 = data[data.year == 2019]
covid = data[data.month <= 3]
covid = covid[covid.day <= 14]
pre_covid = pd.concat([df_2019, covid], join='inner')
# @hidden_cell
fig = go.Figure()
for particle in data.particle.sort_values().unique():
if particle != 'CO' and particle != 'PM10' and particle != 'SO2':
try:
fig.add_trace(go.Scatter(x=data.loc[(data.location == 'Ciutadella') & (data.particle == particle)].hour.sort_values().unique(),
y=data.loc[(data.location == 'Ciutadella') & (data.particle == particle)].groupby('hour')['measure'].mean(),
name=particle, hovertemplate='Measure: %{y:.2f} µg/m3'))
except:
continue
fig.update_layout(
title="Ciutadella",
xaxis_title="Hour of the Day",
yaxis_title="Measure per Cubic Metre",
xaxis = dict(
tickmode = 'linear',
tick0 = 0,
dtick = 2
)
)
fig.show()
# @hidden_cell
fig = plotly.subplots.make_subplots(rows=4, cols=1,
subplot_titles=('NO', 'NO2', 'NOx', 'O3',))
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Ciutadella') & (pre_covid.particle=='NO')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Ciutadella') & (pre_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Pre-Covid'),1,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Ciutadella') & (post_covid.particle=='NO')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Ciutadella') & (post_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Post-Covid'),1,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Ciutadella') & (pre_covid.particle=='NO2')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Ciutadella') & (pre_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Pre-Covid'),2,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Ciutadella') & (post_covid.particle=='NO2')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Ciutadella') & (post_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Post-Covid'),2,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Ciutadella') & (pre_covid.particle=='NOx')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Ciutadella') & (pre_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Pre-Covid'),3,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Ciutadella') & (post_covid.particle=='NOx')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Ciutadella') & (post_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Post-Covid'),3,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Ciutadella') & (pre_covid.particle=='O3')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Ciutadella') & (pre_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Pre-Covid'),4,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Ciutadella') & (post_covid.particle=='O3')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Ciutadella') & (post_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Post-Covid'),4,1)
fig.update_layout(
height=1500,
width=1000,
title="Ciutadella",
showlegend=False
)
fig.update_xaxes(title_text='Hour of the Day', nticks=12)
fig.update_yaxes(title_text='Measure per Cubic Metre')
fig.show()
# @hidden_cell
fig = go.Figure()
for particle in data.particle.sort_values().unique():
if particle != 'CO' and particle != 'PM10' and particle != 'SO2':
try:
fig.add_trace(go.Scatter(x=data.loc[(data.location == 'Eixample') & (data.particle == particle)].hour.sort_values().unique(),
y=data.loc[(data.location == 'Eixample') & (data.particle == particle)].groupby('hour')['measure'].mean(),
name=particle, hovertemplate='Measure: %{y:.2f} µg/m3'))
except:
continue
fig.update_layout(
title="Eixample",
xaxis_title="Hour of the Day",
yaxis_title="Measure per Cubic Metre",
xaxis = dict(
tickmode = 'linear',
tick0 = 0,
dtick = 2
)
)
fig.show()
Eixample is the station which registers the highest levels of air pollution of all. This is logical given its central location and the volume of traffic circulation.
# @hidden_cell
fig = plotly.subplots.make_subplots(rows=4, cols=1,
subplot_titles=('NO', 'NO2', 'NOx', 'O3'))
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Eixample') & (pre_covid.particle=='NO')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Eixample') & (pre_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Pre-Covid'),1,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Eixample') & (post_covid.particle=='NO')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Eixample') & (post_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Post-Covid'),1,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Eixample') & (pre_covid.particle=='NO2')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Eixample') & (pre_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Pre-Covid'),2,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Eixample') & (post_covid.particle=='NO2')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Eixample') & (post_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Post-Covid'),2,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Eixample') & (pre_covid.particle=='NOx')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Eixample') & (pre_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Pre-Covid'),3,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Eixample') & (post_covid.particle=='NOx')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Eixample') & (post_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Post-Covid'),3,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Eixample') & (pre_covid.particle=='O3')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Eixample') & (pre_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Pre-Covid'),4,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Eixample') & (post_covid.particle=='O3')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Eixample') & (post_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Post-Covid'),4,1)
fig.update_layout(
height=1500,
width=1000,
title="Eixample",
showlegend=False
)
fig.update_xaxes(title_text='Hour of the Day', nticks=12)
fig.update_yaxes(title_text='Measure per Cubic Metre')
fig.show()
# @hidden_cell
fig = go.Figure()
for particle in data.particle.sort_values().unique():
if particle != 'CO' and particle != 'PM10' and particle != 'SO2':
try:
fig.add_trace(go.Scatter(x=data.loc[(data.location == 'Gracia') & (data.particle == particle)].hour.sort_values().unique(),
y=data.loc[(data.location == 'Gracia') & (data.particle == particle)].groupby('hour')['measure'].mean(),
name=particle, hovertemplate='Measure: %{y:.2f} µg/m3'))
except:
continue
fig.update_layout(
title="Gracia",
xaxis_title="Hour of the Day",
yaxis_title="Measure per Cubic Metre",
xaxis = dict(
tickmode = 'linear',
tick0 = 0,
dtick = 2
)
)
fig.show()
# @hidden_cell
fig = plotly.subplots.make_subplots(rows=4, cols=1,
subplot_titles=('NO', 'NO2', 'NOx', 'O3'))
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Gracia') & (pre_covid.particle=='NO')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Gracia') & (pre_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Pre-Covid'),1,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Gracia') & (post_covid.particle=='NO')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Gracia') & (post_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Post-Covid'),1,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Gracia') & (pre_covid.particle=='NO2')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Gracia') & (pre_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Pre-Covid'),2,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Gracia') & (post_covid.particle=='NO2')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Gracia') & (post_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Post-Covid'),2,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Gracia') & (pre_covid.particle=='NOx')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Gracia') & (pre_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Pre-Covid'),3,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Gracia') & (post_covid.particle=='NOx')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Gracia') & (post_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Post-Covid'),3,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Gracia') & (pre_covid.particle=='O3')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Gracia') & (pre_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Pre-Covid'),4,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Gracia') & (post_covid.particle=='O3')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Gracia') & (post_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Post-Covid'),4,1)
fig.update_layout(
height=1500,
width=1000,
title="Gracia",
showlegend=False
)
fig.update_xaxes(title_text='Hour of the Day', nticks=12)
fig.update_yaxes(title_text='Measure per Cubic Metre')
fig.show()
# @hidden_cell
fig = go.Figure()
for particle in data.particle.sort_values().unique():
if particle != 'CO' and particle != 'PM10' and particle != 'SO2':
try:
fig.add_trace(go.Scatter(x=data.loc[(data.location == 'Observatori Fabra') & (data.particle == particle)].hour.sort_values().unique(),
y=data.loc[(data.location == 'Observatori Fabra') & (data.particle == particle)].groupby('hour')['measure'].mean(),
name=particle, hovertemplate='Measure: %{y:.2f} µg/m3'))
except:
continue
fig.update_layout(
title="Observatori Fabra",
xaxis_title="Hour of the Day",
yaxis_title="Measure per Cubic Metre",
xaxis = dict(
tickmode = 'linear',
tick0 = 0,
dtick = 2
)
)
fig.show()
The station at Observatori Fabra shows higher levels of O3 than any other contaminant.
# @hidden_cell
fig = plotly.subplots.make_subplots(rows=4, cols=1,
subplot_titles=('NO', 'NO2', 'NOx', 'O3'))
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Observatori Fabra') & (pre_covid.particle=='NO')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Observatori Fabra') & (pre_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Pre-Covid'),1,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Observatori Fabra') & (post_covid.particle=='NO')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Observatori Fabra') & (post_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Post-Covid'),1,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Observatori Fabra') & (pre_covid.particle=='NO2')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Observatori Fabra') & (pre_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Pre-Covid'),2,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Observatori Fabra') & (post_covid.particle=='NO2')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Observatori Fabra') & (post_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Post-Covid'),2,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Observatori Fabra') & (pre_covid.particle=='NOx')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Observatori Fabra') & (pre_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Pre-Covid'),3,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Observatori Fabra') & (post_covid.particle=='NOx')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Observatori Fabra') & (post_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Post-Covid'),3,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Observatori Fabra') & (pre_covid.particle=='O3')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Observatori Fabra') & (pre_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Pre-Covid'),4,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Observatori Fabra') & (post_covid.particle=='O3')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Observatori Fabra') & (post_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Post-Covid'),4,1)
fig.update_layout(
height=1500,
width=1000,
title="Observatori Fabra",
showlegend=False
)
fig.update_xaxes(title_text='Hour of the Day', nticks=12)
fig.update_yaxes(title_text='Measure per Cubic Metre')
fig.show()
Observatori Fabra is the only station where change in the levels of contaminants pre- and post-covid are not as pronounced. Indeed, for O3, it appears the levels of air contamination where better pre-covid.
# @hidden_cell
fig = go.Figure()
for particle in data.particle.sort_values().unique():
if particle != 'CO' and particle != 'PM10' and particle != 'SO2':
try:
fig.add_trace(go.Scatter(x=data.loc[(data.location == 'Palau Reial') & (data.particle == particle)].hour.sort_values().unique(),
y=data.loc[(data.location == 'Palau Reial') & (data.particle == particle)].groupby('hour')['measure'].mean(),
name=particle, hovertemplate='Measure: %{y:.2f} µg/m3'))
except:
continue
fig.update_layout(
title="Palau Reial",
xaxis_title="Hour of the Day",
yaxis_title="Measure per Cubic Metre",
xaxis = dict(
tickmode = 'linear',
tick0 = 0,
dtick = 2
)
)
fig.show()
# @hidden_cell
fig = plotly.subplots.make_subplots(rows=4, cols=1,
subplot_titles=('NO', 'NO2', 'NOx', 'O3'))
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Palau Reial') & (pre_covid.particle=='NO')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Palau Reial') & (pre_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Pre-Covid'),1,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Palau Reial') & (post_covid.particle=='NO')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Palau Reial') & (post_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Post-Covid'),1,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Palau Reial') & (pre_covid.particle=='NO2')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Palau Reial') & (pre_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Pre-Covid'),2,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Palau Reial') & (post_covid.particle=='NO2')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Palau Reial') & (post_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Post-Covid'),2,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Palau Reial') & (pre_covid.particle=='NOx')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Palau Reial') & (pre_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Pre-Covid'),3,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Palau Reial') & (post_covid.particle=='NOx')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Palau Reial') & (post_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Post-Covid'),3,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Palau Reial') & (pre_covid.particle=='O3')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Palau Reial') & (pre_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Pre-Covid'),4,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Palau Reial') & (post_covid.particle=='O3')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Palau Reial') & (post_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Post-Covid'),4,1)
fig.update_layout(
height=1500,
width=1000,
title="Palau Reial",
showlegend=False
)
fig.update_xaxes(title_text='Hour of the Day', nticks=12)
fig.update_yaxes(title_text='Measure per Cubic Metre')
fig.show()
# @hidden_cell
fig = go.Figure()
for particle in data.particle.sort_values().unique():
if particle != 'CO' and particle != 'PM10' and particle != 'SO2':
try:
fig.add_trace(go.Scatter(x=data.loc[(data.location == 'Poblenou') & (data.particle == particle)].hour.sort_values().unique(),
y=data.loc[(data.location == 'Poblenou') & (data.particle == particle)].groupby('hour')['measure'].mean(),
name=particle, hovertemplate='Measure: %{y:.2f} µg/m3'))
except:
continue
fig.update_layout(
title="Poblenou",
xaxis_title="Hour of the Day",
yaxis_title="Measure per Cubic Metre",
xaxis = dict(
tickmode = 'linear',
tick0 = 0,
dtick = 2
)
)
fig.show()
# @hidden_cell
fig = plotly.subplots.make_subplots(rows=3, cols=1,
subplot_titles=('NO', 'NO2', 'NOx'))
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Poblenou') & (pre_covid.particle=='NO')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Poblenou') & (pre_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Pre-Covid'),1,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Poblenou') & (post_covid.particle=='NO')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Poblenou') & (post_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Post-Covid'),1,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Poblenou') & (pre_covid.particle=='NO2')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Poblenou') & (pre_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Pre-Covid'),2,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Poblenou') & (post_covid.particle=='NO2')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Poblenou') & (post_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Post-Covid'),2,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Poblenou') & (pre_covid.particle=='NOx')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Poblenou') & (pre_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Pre-Covid'),3,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Poblenou') & (post_covid.particle=='NOx')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Poblenou') & (post_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Post-Covid'),3,1)
fig.update_layout(
height=1500,
width=1000,
title="Poblenou",
xaxis_title="Hour of the Day",
yaxis_title="Measure per Cubic Metre",
showlegend=False
)
fig.update_xaxes(title_text='Hour of the Day', nticks=12)
fig.update_yaxes(title_text='Measure per Cubic Metre')
fig.show()
# @hidden_cell
fig = go.Figure()
for particle in data.particle.sort_values().unique():
if particle != 'CO' and particle != 'PM10' and particle != 'SO2':
try:
fig.add_trace(go.Scatter(x=data.loc[(data.location == 'Sants') & (data.particle == particle)].hour.sort_values().unique(),
y=data.loc[(data.location == 'Sants') & (data.particle == particle)].groupby('hour')['measure'].mean(),
name=particle, hovertemplate='Measure: %{y:.2f} µg/m3'))
except:
continue
fig.update_layout(
title="Sants",
xaxis_title="Hour of the Day",
yaxis_title="Measure per Cubic Metre",
xaxis = dict(
tickmode = 'linear',
tick0 = 0,
dtick = 2
)
)
fig.show()
# @hidden_cell
fig = plotly.subplots.make_subplots(rows=3, cols=1,
subplot_titles=('NO', 'NO2', 'NOx'))
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Sants') & (pre_covid.particle=='NO')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Sants') & (pre_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Pre-Covid'),1,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Sants') & (post_covid.particle=='NO')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Sants') & (post_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Post-Covid'),1,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Sants') & (pre_covid.particle=='NO2')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Sants') & (pre_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Pre-Covid'),2,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Sants') & (post_covid.particle=='NO2')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Sants') & (post_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Post-Covid'),2,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == 'Sants') & (pre_covid.particle=='NOx')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == 'Sants') & (pre_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Pre-Covid'),3,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == 'Sants') & (post_covid.particle=='NOx')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == 'Sants') & (post_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Post-Covid'),3,1)
fig.update_layout(
height=1500,
width=1000,
title="Sants",
showlegend=False
)
fig.update_xaxes(title_text='Hour of the Day', nticks=12)
fig.update_yaxes(title_text='Measure per Cubic Metre')
fig.show()
# @hidden_cell
fig = go.Figure()
for particle in data.particle.sort_values().unique():
if particle != 'CO' and particle != 'PM10' and particle != 'SO2':
try:
fig.add_trace(go.Scatter(x=data.loc[(data.location == "Vall d'Hebron") & (data.particle == particle)].hour.sort_values().unique(),
y=data.loc[(data.location == "Vall d'Hebron") & (data.particle == particle)].groupby('hour')['measure'].mean(),
name=particle, hovertemplate='Measure: %{y:.2f} µg/m3'))
except:
continue
fig.update_layout(
title="Vall d'Hebron",
xaxis_title="Hour of the Day",
yaxis_title="Measure per Cubic Metre",
xaxis = dict(
tickmode = 'linear',
tick0 = 0,
dtick = 2
)
)
fig.show()
# @hidden_cell
fig = plotly.subplots.make_subplots(rows=4, cols=1,
subplot_titles=('NO', 'NO2', 'NOx', 'O3',))
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == "Vall d'Hebron") & (pre_covid.particle=='NO')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == "Vall d'Hebron") & (pre_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Pre-Covid'),1,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == "Vall d'Hebron") & (post_covid.particle=='NO')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == "Vall d'Hebron") & (post_covid.particle=='NO')].groupby('hour')['measure'].mean(),
name='NO Post-Covid'),1,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == "Vall d'Hebron") & (pre_covid.particle=='NO2')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == "Vall d'Hebron") & (pre_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Pre-Covid'),2,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == "Vall d'Hebron") & (post_covid.particle=='NO2')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == "Vall d'Hebron") & (post_covid.particle=='NO2')].groupby('hour')['measure'].mean(),
name='NO2 Post-Covid'),2,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == "Vall d'Hebron") & (pre_covid.particle=='NOx')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == "Vall d'Hebron") & (pre_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Pre-Covid'),3,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == "Vall d'Hebron") & (post_covid.particle=='NOx')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == "Vall d'Hebron") & (post_covid.particle=='NOx')].groupby('hour')['measure'].mean(),
name='NOx Post-Covid'),3,1)
fig.add_trace(go.Scatter(x=pre_covid.loc[(pre_covid.location == "Vall d'Hebron") & (pre_covid.particle=='O3')].hour.sort_values().unique(),
y=pre_covid.loc[(pre_covid.location == "Vall d'Hebron") & (pre_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Pre-Covid'),4,1)
fig.add_trace(go.Scatter(x=post_covid.loc[(post_covid.location == "Vall d'Hebron") & (post_covid.particle=='O3')].hour.sort_values().unique(),
y=post_covid.loc[(post_covid.location == "Vall d'Hebron") & (post_covid.particle=='O3')].groupby('hour')['measure'].mean(),
name='O3 Post-Covid'),4,1)
fig.update_layout(
height=1500,
width=1000,
title="Vall d'Hebron",
showlegend=False
)
fig.update_xaxes(title_text='Hour of the Day', nticks=12)
fig.update_yaxes(title_text='Measure per Cubic Metre')
fig.show()
At virtually all the stations there was a marked drop in the levels of air pollution during and following confinement.
# @hidden_cell
pm10 = data[data.particle == 'PM10']
pm10['moving_average'] = pm10.groupby('location')['measure'].rolling(window=24).mean().to_list()
pm10.reset_index(inplace=True)
pm10.dropna(subset=['moving_average'], inplace=True)
# @hidden_cell
pm10_ciutadella = pm10[pm10.location == 'Ciutadella']
pm10_eixample = pm10[pm10.location == 'Eixample']
pm10_gracia = pm10[pm10.location == 'Gracia']
pm10_fabra = pm10[pm10.location == 'Observatori Fabra']
pm10_palau = pm10[pm10.location == 'Palau Reial']
pm10_poblenou = pm10[pm10.location == 'Poblenou']
pm10_sants = pm10[pm10.location == 'Sants']
pm10_hebron = pm10[pm10.location == "Vall d'Hebron"]
pm10_ciutadella['moving_average'] = pm10_ciutadella['measure'].rolling(window=24).mean().to_list()
pm10_eixample['moving_average'] = pm10_eixample['measure'].rolling(window=24).mean().to_list()
pm10_gracia['moving_average'] = pm10_gracia['measure'].rolling(window=24).mean().to_list()
pm10_fabra['moving_average'] = pm10_fabra['measure'].rolling(window=24).mean().to_list()
pm10_palau['moving_average'] = pm10_palau['measure'].rolling(window=24).mean().to_list()
pm10_poblenou['moving_average'] = pm10_poblenou['measure'].rolling(window=24).mean().to_list()
pm10_sants['moving_average'] = pm10_sants['measure'].rolling(window=24).mean().to_list()
pm10_hebron['moving_average'] = pm10_hebron['measure'].rolling(window=24).mean().to_list()
# @hidden_cell
fig = go.Figure()
fig.add_trace(go.Scatter(x=pm10_eixample.datetime,
y=pm10_eixample.groupby('datetime')['moving_average'].mean(),
name='Eixample', hovertemplate='Measure: %{y:.2f} µg/m3'))
fig.add_trace(go.Scatter(x=pm10_gracia.datetime,
y=pm10_gracia.groupby('datetime')['moving_average'].mean(),
name='Gracia', hovertemplate='Measure: %{y:.2f} µg/m3'))
fig.add_trace(go.Scatter(x=pm10_fabra.datetime,
y=pm10_fabra.groupby('datetime')['moving_average'].mean(),
name='Observatori Fabra', hovertemplate='Measure: %{y:.2f} µg/m3'))
fig.add_trace(go.Scatter(x=pm10_poblenou.datetime,
y=pm10_poblenou.groupby('datetime')['moving_average'].mean(),
name='Poblenou', hovertemplate='Measure: %{y:.2f} µg/m3'))
fig.add_trace(go.Scatter(x=pm10_sants.datetime,
y=pm10_sants.groupby('datetime')['moving_average'].mean(),
name='Sants', hovertemplate='Measure: %{y:.2f} µg/m3'))
fig.add_trace(go.Scatter(x=pm10_hebron.datetime,
y=pm10_hebron.groupby('datetime')['moving_average'].mean(),
name="Vall d'Hebron", hovertemplate='Measure: %{y:.2f} µg/m3'))
fig.add_trace(go.Scatter(x=pm10_eixample.datetime, y=[50 for i in range(0, len(pm10_eixample.datetime))],
line_dash="dot", name="EU Standards Limit", textposition="bottom center",
fillcolor='black', hovertemplate='50 µg/m3'))
fig.add_trace(go.Scatter(x=pm10_eixample.datetime, y=[20 for i in range(0, len(pm10_eixample.datetime))],
line_dash="dot", name="WHO Guidelines", textposition="bottom center",
hovertemplate='20 µg/m3'))
fig.update_layout(
title="PM10 Values (Average over 24 hours)",
xaxis_title="Date",
yaxis_title="Measure per Cubic Metre",
width=1000,
height=800,
autosize=False,
xaxis=dict(
rangeslider=dict(
visible=True
),
type="date"
)
)
fig.show()
With all stations we again see a marked drop in the amount of air pollution in the period during and following confinement. Worringly, we also observe a number of days where the levels of pollution fari exceed both EU standards and WHO guidelines.
# @hidden_cell
so2 = data[data.particle == 'SO2']
so2['moving_average'] = so2.groupby('location')['measure'].rolling(window=24).mean().to_list()
so2.reset_index(inplace=True)
pm10.dropna(subset=['moving_average'], inplace=True)
# @hidden_cell
so2_eixample = pm10[pm10.location == 'Eixample']
so2_gracia = pm10[pm10.location == 'Gracia']
so2_palau = pm10[pm10.location == 'Palau Reial']
so2_hebron = pm10[pm10.location == "Vall d'Hebron"]
so2_eixample['moving_average'] = so2_eixample['measure'].rolling(window=24).mean().to_list()
so2_gracia['moving_average'] = so2_gracia['measure'].rolling(window=24).mean().to_list()
so2_palau['moving_average'] = so2_palau['measure'].rolling(window=24).mean().to_list()
so2_hebron['moving_average'] = so2_hebron['measure'].rolling(window=24).mean().to_list()
# @hidden_cell
fig = go.Figure()
fig.add_trace(go.Scatter(x=so2_eixample.datetime,
y=so2_eixample.groupby('datetime')['moving_average'].mean(),
name='Eixample', hovertemplate='Measure: %{y:.2f} µg/m3'))
fig.add_trace(go.Scatter(x=so2_gracia.datetime,
y=so2_gracia.groupby('datetime')['moving_average'].mean(),
name='Gracia', hovertemplate='Measure: %{y:.2f} µg/m3'))
fig.add_trace(go.Scatter(x=so2_palau.datetime,
y=so2_palau.groupby('datetime')['moving_average'].mean(),
name='Palau Reial', hovertemplate='Measure: %{y:.2f} µg/m3'))
fig.add_trace(go.Scatter(x=so2_hebron.datetime,
y=so2_hebron.groupby('datetime')['moving_average'].mean(),
name="Vall d'Hebron", hovertemplate='Measure: %{y:.2f} µg/m3'))
fig.add_trace(go.Scatter(x=so2_eixample.datetime, y=[20 for i in range(0, len(so2_eixample.datetime))],
line_dash="dot", name="WHO Guidelines", textposition="bottom center",
hovertemplate='20 µg/m3'))
fig.update_layout(
title="SO2 Values (Average over 24 hours)",
xaxis_title="Date",
yaxis_title="Measure per Cubic Metre",
width=1200,
height=800,
autosize=False,
xaxis=dict(
rangeslider=dict(
visible=True
),
type="date"
)
)
fig.show()
We see the same with SO2 levels. Also concerningly, even during the period of confinement average levels of pollution are above the WHO guidelines.
# @hidden_cell
fig = go.Figure()
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Eixample') & (data.particle == 'NO2')].index.unique())),
y=data.loc[(data.location == 'Eixample') & (data.particle == 'NO2')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} µg/m3', name='NO2'))
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Eixample') & (data.particle == 'NO')].index.unique())),
y=data.loc[(data.location == 'Eixample') & (data.particle == 'NO')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} µg/m3', name='NO'))
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Eixample') & (data.particle == 'O3')].index.unique())),
y=data.loc[(data.location == 'Eixample') & (data.particle == 'O3')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} µg/m3', name='O3'))
fig.update_layout(
title="Eixample",
xaxis_title="Date",
yaxis_title="Measure per Cubic Metre",
autosize=False,
height=800,
width=1000,
xaxis=dict(
rangeslider=dict(
visible=True
),
type="date"
)
)
fig.show()
# @hidden_cell
fig = go.Figure()
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Gracia') & (data.particle == 'NO2')].index.unique())),
y=data.loc[(data.location == 'Gracia') & (data.particle == 'NO2')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} µg/m3', name='NO2'))
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Gracia') & (data.particle == 'NO')].index.unique())),
y=data.loc[(data.location == 'Gracia') & (data.particle == 'NO')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} µg/m3', name='NO'))
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Gracia') & (data.particle == 'O3')].index.unique())),
y=data.loc[(data.location == 'Gracia') & (data.particle == 'O3')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} µg/m3', name='O3'))
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Gracia') & (data.particle == 'CO')].index.unique())),
y=data.loc[(data.location == 'Gracia') & (data.particle == 'CO')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} mg/m3', name='CO'))
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Gracia') & (data.particle == 'SO2')].index.unique())),
y=data.loc[(data.location == 'Gracia') & (data.particle == 'SO2')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} µg/m3', name='SO2'))
fig.update_layout(
title="Gracia",
xaxis_title="Date",
yaxis_title="Measure per Cubic Metre",
autosize=False,
height=800,
width=1000,
xaxis=dict(
rangeslider=dict(
visible=True
),
type="date"
)
)
fig.show()
# @hidden_cell
fig = go.Figure()
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Poblenou') & (data.particle == 'NO2')].index.unique())),
y=data.loc[(data.location == 'Poblenou') & (data.particle == 'NO2')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} µg/m3', name='NO2'))
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Poblenou') & (data.particle == 'NO')].index.unique())),
y=data.loc[(data.location == 'Poblenou') & (data.particle == 'NO')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} µg/m3', name='NO'))
fig.add_trace(go.Scatter(x=pd.Series(sorted(data.loc[(data.location == 'Poblenou') & (data.particle == 'O3')].index.unique())),
y=data.loc[(data.location == 'Poblenou') & (data.particle == 'O3')].groupby('datetime')['measure'].mean(),
hovertemplate='Measure: %{y:.2f} µg/m3', name='O3'))
fig.update_layout(
title="Poblenou",
xaxis_title="Date",
yaxis_title="Measure per Cubic Metre",
autosize=False,
height=800,
width=1000,
xaxis=dict(
rangeslider=dict(
visible=True
),
type="date"
)
)
fig.show()
All stations show decreases in the recordings of levels of contaminantion in the period "post-covid". The most stark difference can be seen in Eixample.
In a period of widespread uncertainty, perhaps lower levels of air pollution are one positive that we can take from the situation. Since we have seen that the impact of covid has had a positive effect on the climate, perhaps we can use it as a starting point to develop new initiatives - such as the Zona de Baixes Emissions - to help reduce pollution and prevent illnesses related to air contamination.